package com.ruixinzheng.kotlindemo

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.ruixinzheng.kotlindemo.databinding.ActivityMainBinding
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException
import kotlin.coroutines.CoroutineContext

class KotlinActivity : AppCompatActivity() {
    private val TAG = "MainActivity"
    private lateinit var binding: ActivityMainBinding
    private val mHandler = Handler(Looper.getMainLooper())
    private val mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        println("onCreate")

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        binding.ctx = this

//        Thread.setDefaultUncaughtExceptionHandler(object : UncaughtExceptionHandler {
//            override fun uncaughtException(t: Thread, e: Throwable) {
//                e.printStackTrace()
//            }
//        })
    }

    override fun onResume() {
        super.onResume()
        println("onResume")
    }

    override fun onDestroy() {
        super.onDestroy()
        println("onDestroy")
    }

    fun executeTasksUseLaunch() {
        println("executeTasksUseLaunch")
        val scope = CoroutineScope(Dispatchers.Main)
        val start = System.currentTimeMillis()
        lifecycleScope.launch {
            println("start task ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
            launch(Dispatchers.IO) {
                println("task1 start ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
                Thread.sleep(1000)
                println("task1 end ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
            }
            launch(Dispatchers.IO) {
                println("task2 start ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
                Thread.sleep(1000)
                println("task2 end ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
            }
            println("after two task ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
        }
        println("outside of scope ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
    }

    /*
    用同步的方式来编写异步的代码
    2个耗时任务依次执行，拿到它们的执行结果后可直接更新ui
     */
    fun executeTasksUseWithContext() {
        println("executeTasksUseWithContext")
        val scope = CoroutineScope(Dispatchers.Main)
        val start = System.currentTimeMillis()
        scope.launch {
            println("start task ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
            val result1 = withContext(Dispatchers.IO) {
                println("task1 ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
                Thread.sleep(1000)
                "a"
            }
            val result2 = withContext(Dispatchers.IO) {
                println("task2 ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
                Thread.sleep(1000)
                "b"
            }
            println("after two task ${Thread.currentThread().name} ${System.currentTimeMillis()-start} result: $result1 $result2")
        }
        println("outside of scope ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
    }

    /*
    用同步的方式来编写异步的代码
    2个耗时任务并发执行，拿到它们的执行结果后可直接更新ui
     */
    fun executeTasksUseAsync() {
        println("executeTasksUseAsync")
        val scope = CoroutineScope(Dispatchers.Main)
        val start = System.currentTimeMillis()
        scope.launch {
            println("start task ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
            val defer1 = async(Dispatchers.IO) {
                println("task1 ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
                Thread.sleep(1000)
                "a"
            }
            val defer2 = async(Dispatchers.IO) {
                println("task2 ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
                Thread.sleep(1000)
                "b"
            }
            val result1 = defer1.await()
            val result2 = defer2.await()
            println("after two task ${Thread.currentThread().name} ${System.currentTimeMillis()-start} result: $result1 $result2")
        }
        println("outside of scope ${Thread.currentThread().name} ${System.currentTimeMillis()-start}")
    }

    /*
    flowOn操作符：用于更改流发射的上下文
     */
    fun useFlow() {
        println("useFlow start")
        val dataFlow = flow {
            println("start emit data ${Thread.currentThread().name}")
            delay(1000)
            emit("abc")
        }.flowOn(Dispatchers.IO)

        lifecycleScope.launch {
            dataFlow.collect { data->
                println("$data ${Thread.currentThread().name}")
            }
        }
        println("useFlow end")
    }

    /*
     runBlocking会阻塞所在线程，只有它和它的子协程执行完成后才会接着走下去
     runBlocking里有一个根协程和子协程，根协程先执行，遇到了挂起函数delay，根协程会被挂起，线程就去执行它的子协程，等它们都执行完才会接着走外面的
     */
    fun testRunBlocking() {
        val startTime = System.currentTimeMillis()
        println("testRunBlocking $startTime")
        runBlocking {
            launch {
                delay(2000)
                println("runBlocking launch ${System.currentTimeMillis()-startTime}")
            }
            delay(1000)
            println("runBlocking ${System.currentTimeMillis()-startTime}")
        }
        println("out of runBlocking ${System.currentTimeMillis()-startTime}")
    }

    /*
     lifecycleScope不会阻塞所在线程，当lifecycleScope里面的协程挂起时，线程就会去执行外面的代码
     */
    fun testLifecycleScope() {
        val startTime = System.currentTimeMillis()
        println("testLifecycleScope $startTime")
        lifecycleScope.launch {
            launch {
                println("child coroutine start ${System.currentTimeMillis()-startTime}")
                delay(2000)
                println("child coroutine end ${System.currentTimeMillis()-startTime}")
            }
            println("root coroutine start ${System.currentTimeMillis()-startTime}")
            delay(2000)
            println("root coroutine end ${System.currentTimeMillis()-startTime}")
        }
        println("out of lifecycleScope ${System.currentTimeMillis()-startTime}")
    }

    fun testLifecycleScopeNoDelay() {
        val startTime = System.currentTimeMillis()
        println("testLifecycleScopeNoDelay $startTime")
        lifecycleScope.launch {
            launch {
                println("child coroutine start ${System.currentTimeMillis()-startTime}")
                println("child coroutine end ${System.currentTimeMillis()-startTime}")
            }
            println("root coroutine start ${System.currentTimeMillis()-startTime}")
            println("root coroutine end ${System.currentTimeMillis()-startTime}")
        }
        println("out of lifecycleScope ${System.currentTimeMillis()-startTime}")
    }

    // 和lifecycleScope类似
    fun testCoroutineScope() {
        val startTime = System.currentTimeMillis()
        println("testCoroutineScope $startTime")
        CoroutineScope(Dispatchers.Main).launch {
            launch {
                delay(2000)
                println("CoroutineScope launch ${System.currentTimeMillis()-startTime}")
            }
            delay(1000)
            println("CoroutineScope ${System.currentTimeMillis()-startTime}")
        }
        println("out of CoroutineScope ${System.currentTimeMillis()-startTime}")
    }

    fun throwException(){
        lifecycleScope.launch {
            println("throw exception")
            throw IOException()
        }
    }

    fun throwInterruptedException(){
        lifecycleScope.launch(Dispatchers.IO) {
            println("throw interrupted exception")
            throw InterruptedException()
        }
    }

    fun throwRuntimeException(){
        lifecycleScope.launch {
            println("throw runtime exception")
            throw RuntimeException()
        }
    }

    fun throwError(){
        lifecycleScope.launch {
            println("throw error")
            throw OutOfMemoryError()
        }
    }

    // 没处理异常虽然能编译通过，但如果没网络会抛出SocketTimeoutException，导致应用crash
    fun testOkHttpException() {
        lifecycleScope.launch(Dispatchers.IO) {
            val okhttp = OkHttpClient.Builder().build()
            val request = Request.Builder().get().url("https://www.baidu.com/").build()
            val response = okhttp.newCall(request).execute()
            val content = response.body()?.string()
            println("request result: $content")
        }
    }

    fun testCoroutineExceptionHandler() {
        val exceptionHandler = object : CoroutineExceptionHandler {
            override val key: CoroutineContext.Key<*> = CoroutineExceptionHandler

            override fun handleException(context: CoroutineContext, exception: Throwable) {
                exception.printStackTrace()
            }
        }
        val coroutineScope = CoroutineScope(Dispatchers.Main + exceptionHandler)
        coroutineScope.launch(Dispatchers.IO) {
            val okhttp = OkHttpClient.Builder().build()
            val request = Request.Builder().get().url("https://www.baidu.com/").build()
            val response = okhttp.newCall(request).execute()
            val content = response.body()?.string()
            println("request result: $content")
        }
    }

    fun cancelScope() {
        lifecycleScope.cancel("cancel scope",CancellationException("cancel by user"))
    }

    fun testCoroutineCanceled() {
        println("testCoroutineCanceled start")
        lifecycleScope.launch {
            println("launch")
        }
        lifecycleScope.launchWhenResumed {
            println("launchWhenResumed")
        }
        lifecycleScope.launch {
            delay(4000)
            println("launch delay")
        }
        println("testCoroutineCanceled end")
    }

    fun testCoroutineCanceled2() {
        println("testCancelCoroutine")

        lifecycleScope.launch(Dispatchers.IO) {
            val startTime = System.currentTimeMillis()
            println("sleep start")
            for (i in 0..3) {
                Thread.sleep(1000) // finish activity后会取消协程，sleep不会被中断，会继续走下去，但未开始的协程不再执行
                //delay(1000) // finish activity后会取消协程，delay会中断并跳出，下面代码不再执行
                println("isActive $isActive isInterrupted ${Thread.currentThread().isInterrupted}")
            }
            println("sleep end isActive $isActive ${System.currentTimeMillis()-startTime}")

            launch {
                println("run in ${Thread.currentThread().name}")
            }
        }
    }

}